/**
 * Reverb
 * <p/>
 * User: druttis
 * Date: 2013-03-18
 * Time: 21:33
 */
package org.dru.masic.effects.reverb {
import flash.events.Event;

import org.dru.masic.MasicComponent;
import org.dru.masic.MasicInput;
import org.dru.masic.MasicOutput;
import org.dru.masic.MasicParameter;

public class Reverb extends MasicComponent {
    private static const BUFFER_SIZE: int = 8192;
    private static const BUFFER_MASK: int = 8191;

    // ports
    private var _in: MasicInput;
    private var _out: MasicOutput;
    // parameters
    private var _delay1: MasicParameter;
    private var _delay2: MasicParameter;
    private var _feedback: MasicParameter;
    private var _mix: MasicParameter;
    // states
    private var _bufL: Vector.<Number>;
    private var _bufR: Vector.<Number>;
    private var _writeIdx: int;
    private var _readIdx0: int;
    private var _readIdx1: int;
    private var _readIdx2: int;
    private var _feedback0: Number;
    private var _feedback1: Number;
    private var _feedback2: Number;

    public function Reverb() {
        // ports
        _in = createInput("in", true);
        _out = createOutput("out", true);
        // parameters
        _delay1 = createParameter("delay1", 0, 0.7, null, null);
        _delay2 = createParameter("delay2", 0, 0.4, null, null);
        _feedback = createParameter("feedback", 0, 0.8, null, null);
        _mix = createParameter("mix", 0, 0.3, null, null);
        // states
        _bufL = new Vector.<Number>(BUFFER_SIZE, true);
        _bufR = new Vector.<Number>(BUFFER_SIZE, true);
        // listeners
        _delay1.addEventListener(Event.CHANGE, onChange);
        _delay2.addEventListener(Event.CHANGE, onChange);
        _feedback.addEventListener(Event.CHANGE, onChange);
        // kick-start
        onChange();
    }

    private function onChange(event: Event = null): void {
        const delay1: Number = (_delay1.value < 0.01 ? 0.01 : (_delay1.value > 0.99 ? 0.99 : _delay1.value));
        const delay2: Number = (_delay2.value < 0.01 ? 0.01 : (_delay2.value > 0.99 ? 0.99 : _delay2.value));
        const feedback: Number = (_feedback.value < -0.99 ? -0.99 : (_feedback.value > 0.99 ? 0.99 : _feedback.value));
        _writeIdx = (_readIdx0 + BUFFER_MASK) & BUFFER_MASK;
        _readIdx1 = (_readIdx0 + BUFFER_MASK * (1.0 - delay1)) & BUFFER_MASK;
        _readIdx2 = (_readIdx0 + BUFFER_MASK * (1.0 - delay2)) & BUFFER_MASK;
        _feedback0 = feedback * 0.2;
        _feedback1 = feedback * 0.3;
        _feedback2 = feedback * 0.5;
    }

    override public function processSamples(start: int, end: int): void {
        // untouched
        const inL: Vector.<Number> = _in.left;
        const inR: Vector.<Number> = _in.right;
        const outL: Vector.<Number> = _out.left;
        const outR: Vector.<Number> = _out.right;
        const bufL: Vector.<Number> = _bufL;
        const bufR: Vector.<Number> = _bufR;
        const feedback0: Number = _feedback0;
        const feedback1: Number = _feedback1;
        const feedback2: Number = _feedback2;
        const wet: Number = _mix.value;
        const dry: Number = 1.0 - wet;
        // update
        var writeIdx: int = _writeIdx;
        var readIdx0: int = _readIdx0;
        var readIdx1: int = _readIdx1;
        var readIdx2: int = _readIdx2;
        // internal
        var tmpB: Number;
        var tmpI: Number;
        for (var index: int = start; index < end; index++) {
            // left
            tmpB = bufL[readIdx0] * feedback0 + bufL[readIdx1] * feedback1 + bufL[readIdx2] * feedback2;
            tmpI = inL[index];
            bufL[writeIdx] = tmpI - tmpB;
            outL[index] = tmpI * dry + tmpB * wet;
            // right
            tmpB = bufR[readIdx0] * feedback0 + bufR[readIdx1] * feedback1 + bufR[readIdx2] * feedback2;
            tmpI = inR[index];
            bufR[writeIdx] = tmpI - tmpB;
            outR[index] = tmpI * dry + tmpB * wet;
            // advance
            writeIdx = (writeIdx + 1) & BUFFER_MASK;
            readIdx0 = (readIdx0 + 1) & BUFFER_MASK;
            readIdx1 = (readIdx1 + 1) & BUFFER_MASK;
            readIdx2 = (readIdx2 + 1) & BUFFER_MASK;
        }
        // update
        _writeIdx = writeIdx;
        _readIdx0 = readIdx0;
        _readIdx1 = readIdx1;
        _readIdx2 = readIdx2;
    }
}
}
